package util

import (
    "encoding/binary"
    "errors"
    "fmt"
    "github.com/cloudwego/hertz/pkg/common/hlog"
)

//气象多要素传感器

// SensorData 解析后的传感器数据
type SensorData struct {
    DeviceAddr  byte
    FuncCode    byte
    ValidBytes  byte
    Humidity    float64 // %RH 湿度
    Temperature float64 // ℃ 温度
    Noise       float64 // dB 噪声
    PM25        float64 // μg/m³
    PM10        float64 // μg/m³
    Pressure    float64 // kPa 大气压 单位：kPa 实际值的10倍
    Light       uint32  // Lux 光照
}

// BuildRequestFrame 构建请求帧
// deviceAddr: 设备地址
// funcCode: 功能码
// startAddr: 起始地址
// dataLength: 数据长度
func BuildRequestFrame(deviceAddr, funcCode byte, startAddr, dataLength uint16) []byte {
    // 打印指令
    hlog.Debugf("构建请求帧: 设备地址: 0x%02X, 功能码: 0x%02X, 起始地址: 0x%04X, 数据长度: 0x%04X", deviceAddr, funcCode, startAddr, dataLength)
    // 构建帧主体
    frame := make([]byte, 6)
    frame[0] = deviceAddr
    frame[1] = funcCode
    binary.BigEndian.PutUint16(frame[2:4], startAddr)
    binary.BigEndian.PutUint16(frame[4:6], dataLength)

    // 计算并添加CRC校验
    crc := calculateCRC(frame)
    frame = append(frame, byte(crc&0xFF))    // CRC低字节
    frame = append(frame, byte(crc>>8&0xFF)) // CRC高字节

    return frame
}

// ParseResponseFrame 解析应答帧
func ParseResponseFrame(response []byte) (*SensorData, error) {
    // 基本长度检查
    if len(response) < 8 {
        return nil, errors.New("应答帧长度不足")
    }
    //校验码低位 0x79  校验码高位0xFD
    // 打印应答帧
    hlog.Debugf("解析应答帧: %v", response)
    // 提取数据部分和CRC
    dataPart := response[:len(response)-2]
    receivedCRC := binary.LittleEndian.Uint16(response[len(response)-2:])

    // 校验CRC
    calculatedCRC := calculateCRC(dataPart)
    if receivedCRC != calculatedCRC {
        return nil, fmt.Errorf("CRC校验失败，接收: 0x%04X, 计算: 0x%04X", receivedCRC, calculatedCRC)
    }

    // 功能码错误检查
    if (response[1] & 0x80) != 0 {
        return nil, fmt.Errorf("设备返回错误，功能码: 0x%02X", response[1])
    }

    // 解析湿度 (2字节)
    humidityRaw := binary.BigEndian.Uint16(response[3:5])
    humidity := float64(humidityRaw) / 10.0

    // 解析温度 (2字节，补码处理)
    tempRaw := binary.BigEndian.Uint16(response[5:7])
    temperature := parseTemperature(tempRaw)

    return &SensorData{
        DeviceAddr:  response[0],
        FuncCode:    response[1],
        ValidBytes:  response[2],
        Humidity:    humidity,
        Temperature: temperature,
    }, nil
}

/*---------- 解析 19 字节应答 ----------*/
//数据为 02031002e3010e012c033c00000000000000034c26
func ParseResponseAll(resp []byte) (*SensorData, error) {
    // 1. 校验应答帧长度（文档6.2节：地址1 + 功能码1 + 有效字节数1 + 数据区16 + CRC2 = 21字节，匹配实际数据长度）
    const expectedFrameLen = 21
    //校验地址码是否正确
    if resp[0] != 0x02 {
        hlog.Debugf("地址码错误，预期0x02，实际0x%x", resp[0])
        return nil, errors.New("地址码错误，预期0x02")
    }
    if len(resp) != expectedFrameLen {
        hlog.Debugf("应答帧长度错误，预期%d字节，实际长度: %d，原始数据: %x", expectedFrameLen, len(resp), resp)
        return nil, errors.New("应答帧长度错误，预期21字节")
    }

    // 2. 校验功能码（文档6.2节：仅支持0x03读取寄存器数据，与500-507号寄存器操作要求一致）
    const supportedFuncCode = 0x03
    if resp[1] != supportedFuncCode {
        hlog.Debugf("功能码错误，仅支持0x03，实际功能码: 0x%x", resp[1])
        return nil, errors.New("功能码错误，仅支持0x03")
    }

    // 3. 校验有效字节数（文档6.2节：有效字节数=数据区长度，16字节数据区对应有效字节数0x10，匹配实际数据）
    const expectedValidBytes = 0x10
    if resp[2] != expectedValidBytes {
        hlog.Debugf("有效字节数错误，预期0x%x（16字节数据区），实际有效字节数: 0x%x", expectedValidBytes, resp[2])
        return nil, errors.New("有效字节数错误，预期0x10")
    }

    // 4. CRC校验（文档6.2节：CRC校验范围为除最后2字节外的所有数据，21字节帧对应前19字节）
    crcCheckRange := resp[:19] // 前19字节：地址1+功能码1+有效字节数1+数据区16
    expectedCRC := calculateCRC(crcCheckRange)
    // 文档6.2节：CRC低位在前、高位在后（倒数第2字节=低位，倒数第1字节=高位）
    actualCRC := uint16(resp[19]) | uint16(resp[20])<<8
    if actualCRC != expectedCRC {
        hlog.Debugf("CRC校验失败，预期0x%x，实际0x%x，校验数据: %x", expectedCRC, actualCRC, crcCheckRange)
        return nil, errors.New("CRC校验失败，数据可能异常")
    }

    // 5. 提取数据区（文档1-70节：500-507号寄存器共8个字段，对应16字节数据区，resp[3:19]为有效数据）
    d := resp[3:19] // 3到19字节（左闭右开），共16字节，匹配数据区长度
    if len(d) != 16 {
        hlog.Debugf("数据区长度错误，预期16字节，实际: %d字节，数据: %x", len(d), d)
        return nil, errors.New("数据区长度错误，预期16字节")
    }

    // 6. 解析气象要素（严格匹配文档1-70节500-507号寄存器定义）
    data := SensorData{
        DeviceAddr:  resp[0],                                           // 地址码（文档6.2节，1字节）
        FuncCode:    resp[1],                                           // 功能码（文档6.2节，固定0x03）
        ValidBytes:  resp[2],                                           // 有效字节数（文档6.2节，固定0x10）
        Humidity:    float64(binary.BigEndian.Uint16(d[0:2])) / 10.0,   // 500号寄存器：湿度×10
        Temperature: parseTemperature(binary.BigEndian.Uint16(d[2:4])), // 501号寄存器：温度×10（补码，文档1-81节）
        Noise:       float64(binary.BigEndian.Uint16(d[4:6])) / 10.0,   // 502号寄存器：噪声×10
        PM25:        float64(binary.BigEndian.Uint16(d[6:8])),          // 503号寄存器：PM2.5实际值
        PM10:        float64(binary.BigEndian.Uint16(d[8:10])),         // 504号寄存器：PM10实际值
        Pressure:    float64(binary.BigEndian.Uint16(d[10:12])) / 10.0, // 505号寄存器：大气压×10（单位KPa）
        Light:       readU32BE(d, 12, 14),                              // 506-507号寄存器：光照高16位+低16位 换算为Lux
    }
    //打印data 根据解释打印 光照 10进制输出
    hlog.Debugf("解析数据: 地址码: %d, 功能码: 0x%02X, 有效字节数: 0x%02X, 湿度: %.1f, 温度: %.1f, 噪声: %.1f, PM2.5: %.1f, PM10: %.1f, 大气压: %.1f, 光照: %d",
        data.DeviceAddr, data.FuncCode, data.ValidBytes, data.Humidity, data.Temperature, data.Noise, data.PM25, data.PM10, data.Pressure, data.Light)
    return &data, nil
}

// readU32BE 把两个连续的 big-endian uint16 拼成 uint32 需要转为10进制
func readU32BE(b []byte, offHi, offLo int) uint32 {
    hi := binary.BigEndian.Uint16(b[offHi:])
    lo := binary.BigEndian.Uint16(b[offLo:])
    return uint32(hi)<<16 | uint32(lo)
}

// 解析温度值（处理补码）
func parseTemperature(raw uint16) float64 {
    // 检查符号位
    if raw&0x8000 != 0 {
        // 负数补码转换
        return float64(int16(raw)) / 10.0
    }
    // 正数直接转换
    return float64(raw) / 10.0
}

// calculateCRC 计算CRC16校验码
func calculateCRC(data []byte) uint16 {
    crc := uint16(0xFFFF)
    for _, b := range data {
        crc ^= uint16(b)
        for i := 0; i < 8; i++ {
            if crc&0x0001 != 0 {
                crc >>= 1
                crc ^= 0xA001 // 多项式
            } else {
                crc >>= 1
            }
        }
    }
    return crc
}
